package cn.lingyangwl.agile.pay.pay.controller.payorder;


import cn.lingyangwl.agile.pay.common.constants.PayNodeEnum;
import cn.lingyangwl.agile.pay.common.service.PayNodeService;
import cn.lingyangwl.agile.pay.common.service.PayOrderService;
import cn.lingyangwl.agile.pay.pay.channel.AbstractChannelNoticeService;
import cn.lingyangwl.agile.pay.pay.channel.IChannelNoticeService;
import cn.lingyangwl.agile.pay.pay.model.bo.MchAppConfigContext;
import cn.lingyangwl.agile.pay.pay.model.rqrs.ChannelRetMsgExt;
import cn.lingyangwl.framework.lock.manager.ILockManager;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import cn.lingyangwl.agile.common.security.annotation.Anonymous;
import cn.lingyangwl.agile.common.security.utils.SecurityUtils;
import cn.lingyangwl.agile.pay.model.entity.PayNode;
import cn.lingyangwl.agile.pay.model.entity.PayOrder;
import cn.lingyangwl.agile.pay.pay.process.ChannelPayOrderProcess;
import cn.lingyangwl.agile.pay.pay.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * 渠道侧的通知入口Controller 【分为同步跳转（doReturn）和异步回调(doNotify) 】
 */
@Slf4j
@Controller
@Anonymous
public class ChannelNoticeController {

    @Resource
    private PayOrderService payOrderService;
    @Resource
    private ConfigContextQueryService configContextQueryService;
    @Resource
    private PayNodeService payNodeService;
    @Resource
    private ChannelPayOrderProcess channelPayOrderProcess;
    @Resource
    private ILockManager lockManager;

    /**
     * 同步通知入口
     * <p>
     * payOrderId 前缀为 ONLYJUMP_，表示直接跳转
     */
    @RequestMapping(value = {"/pay/return/{ifCode}", "/pay/return/{ifCode}/{payOrderNo}"})
    public String doReturn(HttpServletRequest request,
                           @PathVariable("ifCode") String ifCode,
                           @PathVariable(value = "payOrderNo", required = false) String urlOrderNo) {
        String payOrderNo = null;
        String logPrefix = "进入[" + ifCode + "]支付同步跳转：urlOrderNo：[" + StringUtils.defaultIfEmpty(urlOrderNo, "") + "] ";
        log.info("===== {} =====", logPrefix);

        PayNode payNode = PayNode.builder().titleCode(PayNodeEnum.PAY_CALLBACK_OK.getCode())
                .createdUid(SecurityUtils.getUserId())
                .build();

        try {
            // 参数有误
            if (StringUtils.isEmpty(ifCode)) {
                return this.toReturnPage("ifCode is empty");
            }

            // 查询支付接口是否存在
            IChannelNoticeService payNotifyService = AbstractChannelNoticeService.getService(ifCode);

            // 支付通道接口实现不存在
            if (Objects.isNull(payNotifyService)) {
                log.error("{}, interface not exists ", logPrefix);
                return this.toReturnPage("[" + ifCode + "] interface not exists");
            }

            // 仅做跳转，直接跳转订单的returnUrl
//            onlyJump(urlOrderNo, logPrefix);

            // 解析订单号 和 请求参数
            MutablePair<String, Object> mutablePair = payNotifyService.parseParams(request, urlOrderNo,
                    IChannelNoticeService.NoticeTypeEnum.DO_RETURN);
            if (mutablePair == null) { // 解析数据失败， 响应已处理
                log.error("{}, mutablePair is null ", logPrefix);
                throw new BizException("解析数据异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            // 解析到订单号
            payOrderNo = mutablePair.left;
            log.info("{}, 解析数据为：payOrderNo:{}, params:{}", logPrefix, payOrderNo, mutablePair.getRight());

            if (StringUtils.isNotEmpty(urlOrderNo) && !urlOrderNo.equals(payOrderNo)) {
                log.error("{}, 订单号不匹配. urlOrderNo={}, payOrderNo={} ", logPrefix, urlOrderNo, payOrderNo);
                throw new BizException("订单号不匹配！");
            }

            // 获取订单号 和 订单数据
            PayOrder payOrder = payOrderService.getByPayOrderNo(payOrderNo);

            // 订单不存在
            if (payOrder == null) {
                log.error("{}, 订单不存在. payOrderNo={} ", logPrefix, payOrderNo);
                return this.toReturnPage("支付订单不存在");
            }

            // 查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(payOrder.getMchNo(), payOrder.getAppId());

            // 调起接口的回调判断
            ChannelRetMsgExt notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(), payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_RETURN);

            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if (notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null) {
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ", logPrefix, notifyResult);
                throw new BizException("处理回调事件异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            // 判断订单状态
            if (notifyResult.getChannelState() == ChannelRetMsgExt.ChannelState.CONFIRM_SUCCESS) {
                payOrder.setStatus(PayOrder.STATE_SUCCESS);
                payNode.setTitleCode(PayNodeEnum.PAY_CALLBACK_FAIL.getCode());
            } else if (notifyResult.getChannelState() == ChannelRetMsgExt.ChannelState.CONFIRM_FAIL) {
                payOrder.setStatus(PayOrder.STATE_FAIL);
                payNode.setTitleCode(PayNodeEnum.PAY_CALLBACK_OK.getCode());
            }

            boolean hasReturnUrl = StringUtils.isNotBlank(payOrder.getReturnUrl());
            log.info("===== {}, 订单通知完成。 payOrderNo={}, parseState = {}, hasReturnUrl={} =====", logPrefix, payOrderNo, notifyResult.getChannelState(), hasReturnUrl);

            payNodeService.save(payNode);

            // 包含通知地址时
            if (hasReturnUrl) {
                // 重定向
//                response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder, mchAppConfigContext.getMchApp().getAppSecret()));
                return null;
            } else {
                // 跳转到支付成功页面
                return this.toReturnPage(null);
            }

        } catch (Exception e) {
            log.error("{}, payOrderNo={}, exception", logPrefix, payOrderNo, e);
            payNode.setTitleCode(PayNodeEnum.REFUND_CALLBACK_FAIL.getCode());
            payNodeService.save(payNode);
            return this.toReturnPage(e.getMessage());
        }
    }

    /**
     * 异步回调入口
     */
    @ResponseBody
    @RequestMapping(value = {"/pay/notify/{ifCode}", "/pay/notify/{ifCode}/{payOrderNo}"})
    public ResponseEntity<?> doNotify(HttpServletRequest request,
                                      @PathVariable("ifCode") String ifCode,
                                      @PathVariable(value = "payOrderNo", required = false) String urlOrderNo) {

        String payOrderNo = null;
        String logPrefix = "进入[" + ifCode + "]支付回调：urlOrderNo：[" + StringUtils.defaultIfEmpty(urlOrderNo, "") + "] ";
        log.info("===== {} =====", logPrefix);
        PayNode payNode = PayNode.builder().titleCode(PayNodeEnum.PAY_CALLBACK_OK.getCode())
                .createdUid(SecurityUtils.getUserId())
                .build();

        try {
            // 参数有误
            if (StringUtils.isEmpty(ifCode)) {
                return ResponseEntity.badRequest().body("ifCode is empty");
            }

            // 查询支付接口是否存在
            IChannelNoticeService payNotifyService = AbstractChannelNoticeService.getService(ifCode);

            // 支付通道接口实现不存在
            if (payNotifyService == null) {
                log.error("{}, interface not exists ", logPrefix);
                return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
            }

            // 解析订单号 和 请求参数
            MutablePair<String, Object> mutablePair = payNotifyService.parseParams(request, urlOrderNo, IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);
            if (Objects.isNull(mutablePair)) { // 解析数据失败， 响应已处理
                log.error("{}, mutablePair is null ", logPrefix);
                throw new BizException("解析数据异常！"); // 需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            //解析到订单号
            payOrderNo = mutablePair.left;
            log.info("{}, 解析数据为：payOrderNo:{}, params:{}", logPrefix, payOrderNo, mutablePair.getRight());

            if (StringUtils.isNotEmpty(urlOrderNo) && !urlOrderNo.equals(payOrderNo)) {
                log.error("{}, 订单号不匹配. urlOrderNo={}, payOrderNo={} ", logPrefix, urlOrderNo, payOrderNo);
                throw new BizException("订单号不匹配！");
            }

            // 获取订单号 和 订单数据
            PayOrder payOrder = payOrderService.getByPayOrderNo(payOrderNo);

            // 订单不存在
            if (payOrder == null) {
                log.error("{}, 订单不存在. payOrderNo={} ", logPrefix, payOrderNo);
                return payNotifyService.doNotifyOrderNotExists(request);
            }

            // 判断订单是否已经支付成功
            if (PayOrder.STATE_SUCCESS.equals(payOrder.getStatus())) {
                return payNotifyService.doNotifyOrderSuccess(request);
            }

            payNode.setPayOrderNo(payOrderNo);

            // 查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(payOrder.getMchNo(), payOrder.getAppId());

            // 调起接口的回调判断
            ChannelRetMsgExt notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(), payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);

            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if (notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null) {
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ", logPrefix, notifyResult);
                throw new BizException("处理回调事件异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            boolean updateOrderSuccess = true; //默认更新成功
            // 订单是 【支付中状态】
            if (Objects.equals(payOrder.getStatus(), PayOrder.STATE_ING)) {
                // 明确成功
                if (ChannelRetMsgExt.ChannelState.CONFIRM_SUCCESS == notifyResult.getChannelState()) {
                    updateOrderSuccess = payOrderService.updateIng2Success(payOrderNo, notifyResult);
                    payNode.setTitleCode(PayNodeEnum.PAY_CALLBACK_OK.getCode());
                    // 明确失败
                } else if (ChannelRetMsgExt.ChannelState.CONFIRM_FAIL == notifyResult.getChannelState()) {
                    updateOrderSuccess = payOrderService.updateIng2Fail(payOrderNo, notifyResult);
                    payNode.setTitleCode(PayNodeEnum.PAY_CALLBACK_FAIL.getCode());
                }
            }

            // 更新订单 异常
            if (!updateOrderSuccess) {
                log.error("{}, updateOrderSuccess = {} ", logPrefix, false);
                return payNotifyService.doNotifyOrderStateUpdateFail(request);
            }

            // 订单支付成功 其他业务逻辑
            if (notifyResult.getChannelState() == ChannelRetMsgExt.ChannelState.CONFIRM_SUCCESS) {
                payNode.setTitleCode(PayNodeEnum.PAY_CALLBACK_OK.getCode());
                channelPayOrderProcess.confirmSuccess(payOrder);
            }

            log.info("===== {}, 订单通知完成。 payOrderNo={}, parseState = {} =====", logPrefix, payOrderNo, notifyResult.getChannelState());
            return notifyResult.getResponseEntity();
        } catch (Exception e) {
            log.error("{}, payOrderNo={}, exception", logPrefix, payOrderNo, e);
            payNode.setTitleCode(PayNodeEnum.PAY_CALLBACK_FAIL.getCode());

            return ResponseEntity.badRequest().body(e.getMessage());
        } finally {
            payNodeService.savePayNode(payNode);
        }
    }

    /*  跳转到支付成功页面 **/
    private String toReturnPage(String errInfo) {
        return "cashier/returnPage";
    }

//    private void onlyJump(String urlOrderNo, String logPrefix) throws IOException {
//
//        if (StringUtils.isNotBlank(urlOrderNo) && urlOrderNo.startsWith(CS.PAY_RETURNURL_FIX_ONLY_JUMP_PREFIX)) {
//
//            String payOrderNo = urlOrderNo.substring(CS.PAY_RETURNURL_FIX_ONLY_JUMP_PREFIX.length());
//
//            //获取订单号 和 订单数据
//            PayOrder payOrder = payOrderService.getById(payOrderNo);
//
//            // 订单不存在
//            if(payOrder == null){
//                log.error("{}, 订单不存在. payOrderNo={} ", logPrefix, payOrderNo);
//                this.toReturnPage("支付订单不存在");
//            }
//
//            //查询出商户应用的配置信息
//            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(payOrder.getMchNo(), payOrder.getAppId());
//
//            if (StringUtils.isBlank(payOrder.getReturnUrl())) {
//                this.toReturnPage(null);
//            }
//            response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder, mchAppConfigContext.getMchApp().getAppSecret()));
//        }
//    }

}
